package com.black.knight.chess.domain;

import java.io.Serializable;
import java.util.ArrayList;

import com.black.knight.chess.R;
import com.black.knight.chess.model.SahModel;
import com.black.knight.chess.utils.ChessUtil;

public class Move implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private int row;
	private int column;
	
	private int newRow;
	private int newColumn;
	
	private int destroyedFigure = R.drawable.selector;
	private int movedFigure;
	private int derivedFigure;
	
	private boolean castle = false;
	private boolean anPasan = false;
	private boolean promote = false;
	private boolean checkmate = false;
	private boolean pat = false;
	private boolean draw = false;
	private boolean check = false;
	private boolean capture = false;
	private boolean timeout = false;
	private boolean endGame = false;
	private String result = "";
	
	private String PGNMove = "";
	private int pgnType = -1;
	
	public Move(int row, int column) {
		this.row = row;
		this.column = column;
	}
	
	public Move(int position) {
		this.row = position / 8;
		this.column = position % 8;
	}

	public void setNewPosition(int position) {
		this.newRow = position / 8;
		this.newColumn = position % 8;
	}
	
	public int getPosition() {
		return row*8 + column;
	}
	public int getNewPosition() {
		return newRow*8 + newColumn;
	}
	
	public int getRow() {
		return row;
	}
	public void setRow(int row) {
		this.row = row;
	}
	
	public int getColumn() {
		return column;
	}
	
	public void setColumn(int column) {
		this.column = column;
	}
	
	public int getFigure() {
		return SahModel.getInstance().getFigure(row, column);
	}
	
	public int getNewFigure() {
		return SahModel.getInstance().getFigure(newRow, newColumn);
	}
	
	public int getNewRow() {
		return newRow;
	}
	
	public void setNewRow(int newRow) {
		this.newRow = newRow;
	}
	
	public int getNewColumn() {
		return newColumn;
	}
	
	public void setNewColumn(int newColumn) {
		this.newColumn = newColumn;
	}
	
	public int getDestroyedFigure() {
		return destroyedFigure;
	}

	public void setDestroyedFigure(int destroyedFigure) {
		this.destroyedFigure = destroyedFigure;
		if(destroyedFigure != R.drawable.selector && destroyedFigure != R.drawable.empty) {
			capture = true;
		}
	}
	
	public int getMovedFigure() {
		return movedFigure;
	}

	public void setMovedFigure(int movedFigure) {
		this.movedFigure = movedFigure;
	}
	
	public static boolean isFigureBlack(int figure) {
		return figure == R.drawable.bb || figure == R.drawable.bk || figure == R.drawable.bn || figure == R.drawable.bp || figure == R.drawable.bq || figure == R.drawable.br;
	}
	
	public static boolean isFigureWhite(int figure) {
		return figure == R.drawable.wb || figure == R.drawable.wk || figure == R.drawable.wn || figure == R.drawable.wp || figure == R.drawable.wq || figure == R.drawable.wr;
	}
	
	public static boolean isKing(int figure) {
		return figure == R.drawable.wk || figure == R.drawable.bk;
	}
	
	public static boolean isBlackKing(int figure) {
		return figure == R.drawable.bk;
	}
	
	public static boolean isWhiteKing(int figure) {
		return figure == R.drawable.wk;
	}
	
	public static boolean isFigure(int figure) {
		return isFigureBlack(figure) || isFigureWhite(figure);
	}
	
	public boolean check() {
		int figure = getFigure();			
		switch(figure) {
			case R.drawable.wp:
				if(column == newColumn) {
					if(row == newRow + 1 && !isFigure(SahModel.getInstance().getFigure(newRow, newColumn))) {
						setPromote(newRow == 0);						
						return true;
					} else if(row == 6 && row == newRow + 2 && !isFigure(SahModel.getInstance().getFigure(newRow + 1, newColumn)) && !isFigure(SahModel.getInstance().getFigure(newRow, newColumn))) {
						return true;
					}
				} else if((column == newColumn + 1 || column == newColumn - 1) && row == newRow + 1 && isFigureBlack(getNewFigure())) {
					setPromote(newRow == 0);
					return true;
				} else {
					Move result = isAnPasan(true);
					setAnPasan(result != null);
					if(result != null) setDestroyedFigure(result.getMovedFigure());
					return result != null;
				}
				break;
			case R.drawable.bp:
				if(column == newColumn) {
					if(row == newRow - 1 && !isFigure(SahModel.getInstance().getFigure(newRow, newColumn))) {
						setPromote(newRow == 7);
						return true;
					} else if(row == 1 && row == newRow - 2 && !isFigure(SahModel.getInstance().getFigure(newRow - 1, newColumn)) && !isFigure(SahModel.getInstance().getFigure(newRow, newColumn))) {
						return true;
					}
				} else if((column == newColumn + 1 || column == newColumn - 1) && row == newRow - 1 && isFigureWhite(getNewFigure())) {
					setPromote(newRow == 7);
					return true;
				} else {
					Move result = isAnPasan(false);
					setAnPasan(result != null);
					if(result != null) setDestroyedFigure(result.getMovedFigure());
					return result != null;
				}
				break;
			case R.drawable.wr:
			case R.drawable.br:
				return checkTop();
			case R.drawable.wb:
			case R.drawable.bb:
				return checkLaufer();
			case R.drawable.wq:
			case R.drawable.bq:
				return checkLaufer() || checkTop();
			case R.drawable.wn:
			case R.drawable.bn:
				return checkKonj();
			case R.drawable.wk:
			case R.drawable.bk:
				return checkKralj();
		}
		
		return false;
	}
	
	public Move isAnPasan(boolean white) {
		Game game = SahModel.getInstance().getGame();
		
		if(game.getMoves().size() == 0 || game.getSelectedIndex() == 0) return null;
		
		Move lastMove = game.getMoves().get(game.getSelectedIndex() - 1);
		
		Move anPasanMove = new Move(0);
		
		if(white && row == newRow + 1 && isFigureBlack(SahModel.getInstance().getFigure(row, newColumn)) && SahModel.getInstance().getFigure(row, newColumn) == R.drawable.bp) {
			if(column == newColumn + 1 && lastMove.getNewRow() == getRow() && lastMove.getNewColumn() == getNewColumn() && Math.abs(lastMove.getRow() - lastMove.getNewRow()) == 2) {
				anPasanMove.setColumn(newColumn + 1);
				anPasanMove.setRow(newRow + 1);
				return anPasanMove;
			} else if(column == newColumn - 1 && lastMove.getNewRow() == getRow() && lastMove.getNewColumn() == getNewColumn() && Math.abs(lastMove.getRow() - lastMove.getNewRow()) == 2) {
				anPasanMove.setColumn(newColumn - 1);
				anPasanMove.setRow(newRow + 1);
				return anPasanMove;				
			}
		} else if(!white && row == newRow - 1 && isFigureWhite(SahModel.getInstance().getFigure(row, newColumn)) && SahModel.getInstance().getFigure(row, newColumn) == R.drawable.wp) {			
			if(column == newColumn + 1 && lastMove.getNewRow() == getRow() && lastMove.getNewColumn() == getNewColumn() && Math.abs(lastMove.getRow() - lastMove.getNewRow()) == 2) {
				anPasanMove.setColumn(newColumn + 1);
				anPasanMove.setRow(newRow - 1);
				return anPasanMove;
			} else if(column == newColumn - 1 && lastMove.getNewRow() == getRow() && lastMove.getNewColumn() == getNewColumn() && Math.abs(lastMove.getRow() - lastMove.getNewRow()) == 2) {
				anPasanMove.setColumn(newColumn - 1);
				anPasanMove.setRow(newRow - 1);
				return anPasanMove;				
			}
		}
		
		return null;
	}
	
	public boolean isSah() {
		Move kraljPosition = getKraljPosition(SahModel.getInstance().getGame().isWhitePlay());
		
		return kraljPosition.isTucenoPolje(Move.isFigureWhite(kraljPosition.getNewPosition()));
	}
	
	public boolean isSahMat() {
		Game game = SahModel.getInstance().getGame();
		
		for(int i = 0; i<=7; i++) {
			for(int j = 0; j<=7; j++) {
				Move move = new Move(i, j);
				
				if(Move.isFigureWhite(move.getFigure()) !=game.isWhitePlay()) continue;
				ArrayList<Move> moves = move.getPosibleMoves();
				
				for (Move move2 : moves) {
					move.setNewRow(move2.getNewRow());
					move.setNewColumn(move2.getNewColumn());
					
					if(move.getRow() == move.getNewRow() && move.getColumn() == move.getNewColumn()) continue;
					
					move.execute(game.getSelectedIndex() + 1, false);
					
					boolean result = isSah();
					
					SahModel.getInstance().play(game.getSelectedIndex() + 1, true);
					
					game.togglePlayer();
					game.getMoves().remove(game.getMoves().size() - 1);
					
					if(!result) return false;
				}
			}
		}
		
		return true;
	}
	
	public boolean isTucenoPolje(boolean white) {
		Game game = SahModel.getInstance().getGame();
		
		for(int i = 0; i<=7; i++) {
			for(int j = 0; j<=7; j++) {
				setRow(i);
				setColumn(j);
				
				if((isFigureWhite(getNewFigure()) == isFigureBlack(getFigure())) != white) { 
					game.togglePlayer();
					if(check()) {
						game.togglePlayer();
						return true;
					}
					game.togglePlayer();
				}
			}
		}
		
		return false;		
	}
	
	private Move getKraljPosition(boolean isWhite) {
		Move[] moves;
		if(isWhite) {
			moves = getFigurePositions(R.drawable.wk);
		} else {
			moves = getFigurePositions(R.drawable.bk);
		}
		
		if(moves.length == 1) return moves[0]; else return null;
	}
	
	private Move[] getFigurePositions(int f) {
		ArrayList<Move> list = new ArrayList<Move>();
		
		for(int i = 0; i<=7; i++) {
			for(int j = 0; j<=7; j++) {
				int figure = SahModel.getInstance().getFigure(i, j);
				if(figure == f) {
					Move move = new Move(0, 0);
					move.setNewRow(i);
					move.setNewColumn(j);
					
					list.add(move);
				}
			}
		}
		
		return list.toArray(new Move[list.size()]);
	}
	
	public ArrayList<Move> getPosibleMoves() {
		ArrayList<Move> list = new ArrayList<Move>();
		
		for(int i = 0; i<=7; i++) {
			for(int j = 0; j<=7; j++) {
				Move newMove = new Move(getRow(), getColumn());
				
				newMove.setNewRow(i);
				newMove.setNewColumn(j);
				
				if(newMove.check()) {
					list.add(newMove);
				}
			}
		}
		
		Move newMove = new Move(getRow(), getColumn());
		newMove.setNewRow(newMove.getRow());
		newMove.setNewColumn(newMove.getColumn());
		
		list.add(newMove);
		
		return list;
	}
	
	private boolean checkLaufer() {
		if(column - newColumn == row - newRow) {
			int minRow = row;
			int maxRow = newRow;
			int minCol = column;
			
			if(minRow > newRow) {
				minRow = newRow;
				maxRow = row;
				minCol = newColumn;
			}
			
			for(int i = minRow + 1, j = minCol + 1; i<=maxRow-1; i++, j++) {
				if(isFigure(SahModel.getInstance().getFigure(i, j))) { 
					return false;
				}
			}
		} else if(column - newColumn == newRow - row) {
			int minRow = row;
			int maxRow = newRow;
			int minCol = column;
			
			if(minRow > newRow) {
				minRow = newRow;
				maxRow = row;
				minCol = newColumn;
			}
			
			for(int i = minRow + 1, j = minCol - 1; i<=maxRow-1; i++, j--) {
				if(isFigure(SahModel.getInstance().getFigure(i, j))) { 
					return false;
				}
			}
		} else return false;
		
		return !(SahModel.getInstance().isWhitePlay() && isFigureWhite(getNewFigure()) || !SahModel.getInstance().isWhitePlay() && isFigureBlack(getNewFigure()));
	}
	
	private boolean checkKonj() {
		if(column == newColumn - 1 && row == newRow - 2 ||
		       column == newColumn - 2 && row == newRow - 1 ||
		       column == newColumn + 2 && row == newRow + 1 ||
		       column == newColumn + 1 && row == newRow + 2 ||
		       column == newColumn + 1 && row == newRow - 2 ||
		       column == newColumn + 2 && row == newRow - 1 ||
		       column == newColumn - 2 && row == newRow + 1 ||
		       column == newColumn - 1 && row == newRow + 2) {
			return !(SahModel.getInstance().isWhitePlay() && isFigureWhite(getNewFigure()) || !SahModel.getInstance().isWhitePlay() && isFigureBlack(getNewFigure()));
		}
		
		return false;
	}
	
	private boolean checkKralj() {
		boolean result = false;
		
		if(column - newColumn < 2 && newColumn - column < 2 && row - newRow < 2 && newRow - row < 2) {
			result = !(SahModel.getInstance().isWhitePlay() && isFigureWhite(getNewFigure()) || !SahModel.getInstance().isWhitePlay() && isFigureBlack(getNewFigure()));
		} else if(SahModel.getInstance().isWhitePlay() && SahModel.getInstance().getGame().isWhiteRokadaValid(this)) {
			if(newRow == 7 && newColumn == 6 && !isFigure(SahModel.getInstance().getFigure(7, 5)) && !isFigure(SahModel.getInstance().getFigure(7, 6)) && SahModel.getInstance().getFigure(7, 7) == R.drawable.wr) {
				Move move = new Move(0, 0);
				move.setNewRow(7);
				move.setNewColumn(6);
				Move move1 = new Move(0, 0);
				move1.setNewRow(7);
				move1.setNewColumn(5);
				
				result = !isSah() && !move.isTucenoPolje(true) && !move1.isTucenoPolje(true);
				
				setCastle(result);
			} else if(newRow == 7 && newColumn == 2 && !isFigure(SahModel.getInstance().getFigure(7, 1)) && !isFigure(SahModel.getInstance().getFigure(7, 2)) && !isFigure(SahModel.getInstance().getFigure(7, 3)) && SahModel.getInstance().getFigure(7, 0) == R.drawable.wr) {
				Move move = new Move(0, 0);
				move.setNewRow(7);
				move.setNewColumn(1);
				Move move1 = new Move(0, 0);
				move1.setNewRow(7);
				move1.setNewColumn(2);
				Move move2 = new Move(0, 0);
				move2.setNewRow(7);
				move2.setNewColumn(3);
				
				result = !isSah() && !move.isTucenoPolje(true) && !move1.isTucenoPolje(true) && !move2.isTucenoPolje(true);
				
				setCastle(result);
			}
		} else if(!SahModel.getInstance().isWhitePlay() && SahModel.getInstance().getGame().isBlackRokadaValid(this)) {
			if(newRow == 0 && newColumn == 6 && !isFigure(SahModel.getInstance().getFigure(0, 5)) && !isFigure(SahModel.getInstance().getFigure(0, 6)) && SahModel.getInstance().getFigure(0, 7) == R.drawable.br) {
				Move move = new Move(0, 0);
				move.setNewRow(0);
				move.setNewColumn(6);
				Move move1 = new Move(0, 0);
				move1.setNewRow(0);
				move1.setNewColumn(5);
				
				result = !isSah() && !move.isTucenoPolje(false) && !move1.isTucenoPolje(false);
				
				setCastle(result);
			} else if(newRow == 0 && newColumn == 2 && !isFigure(SahModel.getInstance().getFigure(0, 1)) && !isFigure(SahModel.getInstance().getFigure(0, 2)) && !isFigure(SahModel.getInstance().getFigure(0, 3)) && SahModel.getInstance().getFigure(0, 0) == R.drawable.br) {
				Move move = new Move(0, 0);
				move.setNewRow(0);
				move.setNewColumn(1);
				Move move1 = new Move(0, 0);
				move1.setNewRow(0);
				move1.setNewColumn(2);
				Move move2 = new Move(0, 0);
				move2.setNewRow(0);
				move2.setNewColumn(3);
				
				result = !isSah() && !move.isTucenoPolje(false) && !move1.isTucenoPolje(false) && !move2.isTucenoPolje(false);
				
				setCastle(result);
			}
		}
		
		return result;
	}
	
	private boolean checkTop() {
		if(column == newColumn && row != newRow) {
			int min = row;
			int max = newRow;
			if(min > newRow) {
				min = newRow;
				max = row;
			}
			
			for(int i = min + 1; i<=max-1; i++) {
				if(isFigure(SahModel.getInstance().getFigure(i, column))) return false;
			}
		} else if(column != newColumn && row == newRow) {
			int min = column;
			int max = newColumn;
			if(min > newColumn) {
				min = newColumn;
				max = column;
			}
			
			for(int i = min + 1; i<=max-1; i++) {
				if(isFigure(SahModel.getInstance().getFigure(row, i))) return false;
			}					
		} else return false;
		
		return !(SahModel.getInstance().isWhitePlay() && isFigureWhite(getNewFigure()) || !SahModel.getInstance().isWhitePlay() && isFigureBlack(getNewFigure()));
	}
	
	public void execute(int movePosition, boolean processPGN) {
		setDestroyedFigure(getNewFigure());
		movedFigure = getFigure();
		
		if(processPGN) preProcessPGN();
		
		SahModel.getInstance().moveFigure(this, movePosition);
	}
	
	public String fromField() {
		return ChessUtil.getChessCoords(column, row);
	}
	
	public String toField() {
		return ChessUtil.getChessCoords(newColumn, newRow);
	}
	
	public void preProcessPGN() {
		if(moveType1()) pgnType = 1;
		else if(moveType2()) pgnType = 2;
		else if(moveType3()) pgnType = 3;
		else if(moveType5()) pgnType = 5;
		else if(moveType4()) pgnType = 4;
		else pgnType = 6;
	}
	
	public void postProcessPGN() {
		StringBuffer pgnMove = new StringBuffer();
		
		String cap = capture ? "x" : "";
		String ch = check ? "+" : "";
		String checkm = checkmate ? "#" : "";
		String pro = promote ? "=" + ChessUtil.getFigureFlag(getDerivedFigure()) : "";
		
		if(isEndGame() && getResult().length() > 0) {
			pgnMove.append(getResult());
		} else if(castle) {
			if(newColumn == 6) pgnMove.append("O-O").append(pro).append(ch).append(checkm);
			else if(newColumn == 2) pgnMove.append("O-O-O").append(pro).append(ch).append(checkm);
		} else if(pgnType == 1) {
			pgnMove.append(ChessUtil.getChessCoords(newColumn, newRow)).append(pro).append(ch).append(checkm);
		} else if(pgnType == 2) {
			pgnMove.append(ChessUtil.getColumnFlag(column)).append(cap).append(ChessUtil.getChessCoords(newColumn, newRow)).append(pro).append(ch).append(checkm);
		} else if(pgnType == 3) {
			pgnMove.append(ChessUtil.getFigureFlag(getMovedFigure())).append(cap).append(ChessUtil.getChessCoords(newColumn, newRow)).append(pro).append(ch).append(checkm);
		} else if(pgnType == 4) {
			pgnMove.append(ChessUtil.getFigureFlag(getMovedFigure())).append(cap).append(8 - row).append(ChessUtil.getChessCoords(newColumn, newRow)).append(pro).append(ch).append(checkm);
		} else if(pgnType == 5) {
			pgnMove.append(ChessUtil.getFigureFlag(getMovedFigure())).append(cap).append(ChessUtil.getColumnFlag(column)).append(ChessUtil.getChessCoords(newColumn, newRow)).append(pro).append(ch).append(checkm);
		} else if(pgnType == 6) {
			pgnMove.append(ChessUtil.getFigureFlag(getMovedFigure())).append(cap).append(ChessUtil.getChessCoords(column, row)).append(ChessUtil.getChessCoords(newColumn, newRow)).append(pro).append(ch).append(checkm);
		}
		
		setPGNMove(pgnMove.toString());
	}
	
	private boolean moveType1() {
		return (movedFigure == R.drawable.bp || movedFigure == R.drawable.wp) && column == newColumn;
	}
	
	private boolean moveType2() {
		return movedFigure == R.drawable.bp || movedFigure == R.drawable.wp && capture;
	}
	
	private boolean moveType3() {
		Move[] moves = getFigurePositions(getMovedFigure());
		
		if(moves.length == 1) {
			return true;
		}
		
		return getMovesToField(moves).length == 1;
	}
		
	private boolean moveType4() {
		Move[] moves = getFigurePositions(getMovedFigure());
		
		int counter = 0;
		for (Move move : moves) {
			if(row == move.getNewRow()) counter++;
		}
		
		return counter == 1;
	}
	
	private boolean moveType5() {
		Move[] moves = getFigurePositions(getMovedFigure());
		
		int counter = 0;
		for (Move move : moves) {
			if(column == move.getNewColumn()) counter++;
		}
		
		return counter == 1;
	}
	
	private Move[] getMovesToField(Move[] moves) {
		ArrayList<Move> list = new ArrayList<Move>();
		
		for (Move move : moves) {
			Move checkMove = new Move(move.getNewRow(), move.getNewColumn());
			ArrayList<Move> possibleMoves = checkMove.getPosibleMoves();
			
			for (Move move2 : possibleMoves) {
				if(move2.getNewRow() == newRow && move2.getNewColumn() == newColumn) {
					list.add(move2);
					break;
				}
			}
		}
		
		return list.toArray(new Move[list.size()]);
	}


	public boolean isCastle() {
		return castle;
	}

	public void setCastle(boolean castle) {
		this.castle = castle;
	}

	public boolean isAnPasan() {
		return anPasan;
	}

	public void setAnPasan(boolean anPasan) {
		this.anPasan = anPasan;
	}

	public boolean isPromote() {
		return promote;
	}

	public void setPromote(boolean promote) {
		this.promote = promote;
	}

	public int getDerivedFigure() {
		return derivedFigure;
	}

	public void setDerivedFigure(int derivedFigure) {
		this.derivedFigure = derivedFigure;
	}

	public boolean isCheckmate() {
		return checkmate;
	}

	public void setCheckmate(boolean checkmate) {
		this.checkmate = checkmate;
	}

	public boolean isPat() {
		return pat;
	}

	public void setPat(boolean pat) {
		this.pat = pat;
	}

	public boolean isCheck() {
		return check;
	}

	public void setCheck(boolean check) {
		this.check = check;
	}

	public boolean isCaptured() {
		return capture;
	}

	public void setCaptured(boolean captured) {
		this.capture = captured;
	}

	public boolean isEndGame() {
		return endGame;
	}

	public void setEndGame(boolean endGame) {
		this.endGame = endGame;
	}

	public String getPGNMove() {
		return PGNMove;
	}

	public void setPGNMove(String PGNMove) {
		this.PGNMove = PGNMove;
	}

	public boolean isTimeout() {
		return timeout;
	}

	public void setTimeout(boolean timeout) {
		this.timeout = timeout;
	}

	public String getResult() {
		return result;
	}

	public void setResult(String result) {
		this.result = result;
	}

	public boolean isDraw() {
		return draw;
	}

	public void setDraw(boolean draw) {
		this.draw = draw;
	}
}
